# ******************************************************************************
#  Copyright (c) 2021-2022. Kneron Inc. All rights reserved.                   *
# ******************************************************************************

from .KPBaseClass.StructureBase import BufferBase
from .KPEnum import *
from .KPConstant import Const
from typing import List
import ctypes
import numpy as np


class DeviceDescriptorBuffer(BufferBase):
    """DeviceDescriptor structure"""
    _pack_ = 4
    _fields_ = [('_port_id', ctypes.c_uint32),
                ('_vendor_id', ctypes.c_uint16),
                ('_product_id', ctypes.c_uint16),
                ('_link_speed', ctypes.c_int),
                ('_kn_number', ctypes.c_uint32),
                ('_isConnectable', ctypes.c_bool),
                ('_port_path', ctypes.c_char * 20),
                ('_firmware', ctypes.c_char * 30)]

    def _init_buffer(self,
                     usb_port_id: int = 0,
                     vendor_id: int = 0,
                     product_id: int = 0,
                     link_speed: UsbSpeed = UsbSpeed.KP_USB_SPEED_UNKNOWN,
                     kn_number: int = 0,
                     is_connectable: bool = False,
                     usb_port_path: str = '',
                     firmware: str = '') -> None:
        assert 20 >= len(usb_port_path)
        assert 30 >= len(firmware)

        self._port_id = usb_port_id
        self._vendor_id = vendor_id
        self._product_id = product_id
        self._link_speed = link_speed.value
        self._kn_number = kn_number
        self._isConnectable = is_connectable
        self._port_path = usb_port_path.encode('utf-8')
        self._firmware = firmware.encode('utf-8')


class DeviceDescriptorListBuffer(BufferBase):
    """DeviceDescriptorList structure"""
    _pack_ = 4
    _fields_ = [('_num_dev', ctypes.c_int),
                ('_device', ctypes.POINTER(DeviceDescriptorBuffer))]

    def __init__(self, device_descriptor_buffer_list: List[DeviceDescriptorBuffer] = []):
        """ check if allocated memory from c """
        self._init_buffer(device_descriptor_buffer_list=device_descriptor_buffer_list)

    def _init_buffer(self, device_descriptor_buffer_list: List[DeviceDescriptorBuffer] = []) -> None:
        self._num_dev = len(device_descriptor_buffer_list)
        self.__device_c_array = (DeviceDescriptorBuffer * self._num_dev)(*device_descriptor_buffer_list)
        self._device = ctypes.cast(self.__device_c_array, ctypes.POINTER(DeviceDescriptorBuffer))


class DeviceGroupPointerBuffer(BufferBase):
    """DeviceGroupPointer structure"""
    _pack_ = 4
    _fields_ = [('_address', ctypes.c_void_p)]

    def __init__(self, address: int):
        """ check if allocated memory from c """
        self._init_buffer(address=address)

    def _init_buffer(self, address: int) -> None:
        self._address = ctypes.c_void_p(address)


class BaseVersionBuffer(BufferBase):
    """BaseVersion structure"""
    _pack_ = 4
    _fields_ = [('_major', ctypes.c_uint32),
                ('_minor', ctypes.c_uint32),
                ('_revision', ctypes.c_uint32)]

    def _init_buffer(self,
                     major: int = 0,
                     minor: int = 0,
                     revision: int = 0, ) -> None:
        assert 0 <= major
        assert 0 <= minor
        assert 0 <= revision

        self._major = major
        self._minor = minor
        self._revision = revision


class QuantizedFixedPointDescriptorBuffer(BufferBase):
    """QuantizedFixedPointDescriptor structure"""
    _pack_ = 4
    _fields_ = [('_scale', ctypes.c_float),
                ('_radix', ctypes.c_int32)]

    def _init_buffer(self,
                     scale: float = 0,
                     radix: int = 0) -> None:
        self._scale = scale
        self._radix = radix


class QuantizationParametersBuffer(BufferBase):
    """QuantizationParameters structure"""
    _pack_ = 4
    _fields_ = [('_quantized_fixed_point_descriptor_num', ctypes.c_uint32),
                ('_quantized_fixed_point_descriptor', ctypes.POINTER(QuantizedFixedPointDescriptorBuffer))]

    def _init_buffer(self,
                     quantized_fixed_point_descriptor_num: int = 0,
                     quantized_fixed_point_descriptor_list: List[QuantizedFixedPointDescriptorBuffer] = []) -> None:
        assert 0 <= quantized_fixed_point_descriptor_num

        self._quantized_fixed_point_descriptor_num = quantized_fixed_point_descriptor_num
        self.__quantized_fixed_point_descriptor_c_array = (
                    QuantizedFixedPointDescriptorBuffer * self._quantized_fixed_point_descriptor_num)(
            *quantized_fixed_point_descriptor_list)
        self._quantized_fixed_point_descriptor = ctypes.cast(self.__quantized_fixed_point_descriptor_c_array,
                                                             ctypes.POINTER(QuantizedFixedPointDescriptorBuffer))


class TensorDescriptorBuffer(BufferBase):
    """TensorDescriptor structure"""
    _pack_ = 4
    _fields_ = [('_index', ctypes.c_uint32),
                ('_name', ctypes.c_char_p),
                ('_shape_npu_len', ctypes.c_uint32),
                ('_shape_npu', ctypes.POINTER(ctypes.c_uint32)),
                ('_shape_onnx_len', ctypes.c_uint32),
                ('_shape_onnx', ctypes.POINTER(ctypes.c_uint32)),
                ('_data_layout', ctypes.c_uint32),
                ('_quantization_parameters', QuantizationParametersBuffer)]

    def _init_buffer(self,
                     index: int = 0,
                     name: str = '',
                     shape_npu_len: int = 0,
                     shape_npu: List[int] = [],
                     shape_onnx_len: int = 0,
                     shape_onnx: List[int] = [],
                     data_layout: int = 0,
                     quantization_parameters: QuantizationParametersBuffer = QuantizationParametersBuffer()) -> None:
        assert 0 <= index
        assert 0 <= shape_npu_len
        assert 0 <= shape_onnx_len
        assert -1 <= data_layout

        self._index = index
        self._name = name.encode('utf-8')
        self._shape_npu_len = shape_npu_len
        self._shape_onnx_len = shape_onnx_len
        self._data_layout = data_layout
        self._quantization_parameters = quantization_parameters

        self.__shape_npu_c_array = (ctypes.c_uint32 * self._shape_npu_len)(*shape_npu)
        self._shape_npu = ctypes.cast(self.__shape_npu_c_array, ctypes.POINTER(ctypes.c_uint32))

        self.__shape_onnx_c_array = (ctypes.c_uint32 * self._shape_onnx_len)(*shape_onnx)
        self._shape_onnx = ctypes.cast(self.__shape_onnx_c_array, ctypes.POINTER(ctypes.c_uint32))


class SingleModelDescriptorBuffer(BufferBase):
    """SingleModelDescriptor structure"""
    _pack_ = 4
    _fields_ = [('_target', ctypes.c_uint32),
                ('_version', ctypes.c_uint32),
                ('_id', ctypes.c_uint32),
                ('_input_nodes_num', ctypes.c_uint32),
                ('_input_nodes', ctypes.POINTER(TensorDescriptorBuffer)),
                ('_output_nodes_num', ctypes.c_uint32),
                ('_output_nodes', ctypes.POINTER(TensorDescriptorBuffer)),
                ('_setup_bin_schema_version', BaseVersionBuffer),
                ('_file_schema_version', BaseVersionBuffer),
                ('_max_raw_out_size', ctypes.c_uint32)]

    def _init_buffer(self,
                     target: int = 0,
                     version: int = 0,
                     id: int = 0,
                     input_nodes_num: int = 0,
                     input_nodes: List[TensorDescriptorBuffer] = [],
                     output_nodes_num: int = 0,
                     output_nodes: List[TensorDescriptorBuffer] = [],
                     setup_bin_schema_version: BaseVersionBuffer = BaseVersionBuffer(),
                     file_schema_version: BaseVersionBuffer = BaseVersionBuffer(),
                     max_raw_out_size: int = 0) -> None:
        assert -1 <= target
        assert 0 <= version
        assert 0 <= id
        assert 0 <= input_nodes_num
        assert 0 <= output_nodes_num
        assert 0 <= max_raw_out_size

        self._target = target
        self._version = version
        self._id = id
        self._input_nodes_num = input_nodes_num
        self._output_nodes_num = output_nodes_num
        self._setup_bin_schema_version = setup_bin_schema_version
        self._file_schema_version = file_schema_version
        self._max_raw_out_size = max_raw_out_size

        self.__input_nodes_c_array = (TensorDescriptorBuffer * self._input_nodes_num)(*input_nodes)
        self._input_nodes = ctypes.cast(self.__input_nodes_c_array, ctypes.POINTER(TensorDescriptorBuffer))

        self.__output_nodes_c_array = (TensorDescriptorBuffer * self._output_nodes_num)(*output_nodes)
        self._output_nodes = ctypes.cast(self.__output_nodes_c_array, ctypes.POINTER(TensorDescriptorBuffer))


class ModelNefMetadataBuffer(BufferBase):
    """ModelNefMetadata structure"""
    _pack_ = 4
    _fields_ = [('_kn_num', ctypes.c_uint32),
                ('_toolchain_version', ctypes.c_char_p),
                ('_compiler_version', ctypes.c_char_p),
                ('_nef_schema_version', BaseVersionBuffer),
                ('_platform', ctypes.c_char_p)]

    def _init_buffer(self,
                     kn_num: int = 0,
                     toolchain_version: str = '',
                     compiler_version: str = '',
                     nef_schema_version: BaseVersionBuffer = BaseVersionBuffer(),
                     platform: str = '') -> None:
        assert 0 <= kn_num

        self._kn_num = kn_num
        self._toolchain_version = toolchain_version.encode('utf-8')
        self._compiler_version = compiler_version.encode('utf-8')
        self._nef_schema_version = nef_schema_version
        self._platform = platform.encode('utf-8')


class ModelNefDescriptorBuffer(BufferBase):
    """ModelNefDescriptor List structure"""
    _pack_ = 4
    _fields_ = [('_magic', ctypes.c_uint32),
                ('_metadata', ModelNefMetadataBuffer),
                ('_target', ctypes.c_uint32),
                ('_crc', ctypes.c_uint32),
                ('_num_models', ctypes.c_uint32),
                ('_models', ctypes.POINTER(SingleModelDescriptorBuffer))]

    def _init_buffer(self,
                     magic: int = 0,
                     metadata: ModelNefMetadataBuffer = ModelNefMetadataBuffer(),
                     target: int = 0,
                     crc: int = 0,
                     num_models: int = 0,
                     models: List[SingleModelDescriptorBuffer] = []) -> None:
        assert 0 <= magic
        assert -1 <= target
        assert 0 <= crc
        assert 0 <= num_models

        self._magic = magic
        self._metadata = metadata
        self._target = target
        self._crc = crc
        self._num_models = num_models

        self.__models_c_array = (SingleModelDescriptorBuffer * self._num_models)(*models)
        self._models = ctypes.cast(self.__models_c_array, ctypes.POINTER(SingleModelDescriptorBuffer))


class DdrManageAttributesBuffer(BufferBase):
    """DdrManageAttributes structure"""
    _pack_ = 4
    _fields_ = [('_model_size', ctypes.c_uint32),
                ('_input_buffer_size', ctypes.c_uint32),
                ('_input_buffer_count', ctypes.c_uint32),
                ('_result_buffer_size', ctypes.c_uint32),
                ('_result_buffer_count', ctypes.c_uint32)]

    def _init_buffer(self,
                     model_size: int = 0,
                     input_buffer_size: int = 0,
                     input_buffer_count: int = 0,
                     result_buffer_size: int = 0,
                     result_buffer_count: int = 0) -> None:
        assert 0 <= model_size
        assert 0 <= input_buffer_size
        assert 0 <= input_buffer_count
        assert 0 <= result_buffer_size
        assert 0 <= result_buffer_count

        self._model_size = model_size
        self._input_buffer_size = input_buffer_size
        self._input_buffer_count = input_buffer_count
        self._result_buffer_size = result_buffer_size
        self._result_buffer_count = result_buffer_count


class DeviceGroupBuffer(BufferBase):
    """DeviceGroupBuffer structure"""
    _pack_ = 4
    _fields_ = [('_timeout', ctypes.c_int),
                ('_num_device', ctypes.c_int),
                ('_product_id', ctypes.c_uint32),
                ('_loaded_model_desc', ModelNefDescriptorBuffer),
                ('_ddr_attr', DdrManageAttributesBuffer)]

    def _init_buffer(self,
                     timeout: int,
                     num_device: int,
                     product_id: int,
                     loaded_model_nef_descriptor: ModelNefDescriptorBuffer,
                     ddr_manage_attributes: DdrManageAttributesBuffer) -> None:
        assert 0 <= timeout
        assert 0 <= num_device
        assert 0 <= product_id

        self._timeout = timeout
        self._num_device = num_device
        self._product_id = product_id
        self._loaded_model_desc = loaded_model_nef_descriptor
        self._ddr_attr = ddr_manage_attributes


class FirmwareVersionBuffer(BufferBase):
    """FirmwareVersion structure"""
    _pack_ = 4
    _fields_ = [('_reserved', ctypes.c_uint8),
                ('_major', ctypes.c_uint8),
                ('_minor', ctypes.c_uint8),
                ('_update', ctypes.c_uint8),
                ('_build', ctypes.c_uint32)]

    def _init_buffer(self,
                     reserved: int = 0,
                     major: int = 0,
                     minor: int = 0,
                     update: int = 0,
                     build: int = 0) -> None:
        assert 0 <= reserved
        assert 0 <= major
        assert 0 <= minor
        assert 0 <= update
        assert 0 <= build

        self._reserved = reserved
        self._major = major
        self._minor = minor
        self._update = update
        self._build = build


class SystemInfoBuffer(BufferBase):
    """SystemInfo structure"""
    _pack_ = 4
    _fields_ = [('_kn_number', ctypes.c_uint32),
                ('_firmware_version', FirmwareVersionBuffer)]

    def _init_buffer(self,
                     kn_number: int = 0,
                     firmware_version: FirmwareVersionBuffer = FirmwareVersionBuffer()) -> None:
        assert 0 <= kn_number

        self._kn_number = kn_number
        self._firmware_version = firmware_version


class InfConfigurationBuffer(BufferBase):
    """InfConfiguration structure"""
    _pack_ = 4
    _fields_ = [('_enable_frame_drop', ctypes.c_bool)]

    def _init_buffer(self,
                     enable_frame_drop: bool = False) -> None:
        self._enable_frame_drop = enable_frame_drop


class InfCropBoxBuffer(BufferBase):
    """InfCropBox structure"""
    _pack_ = 4
    _fields_ = [('_crop_number', ctypes.c_uint32),
                ('_x1', ctypes.c_uint32),
                ('_y1', ctypes.c_uint32),
                ('_width', ctypes.c_uint32),
                ('_height', ctypes.c_uint32)]

    def _init_buffer(self,
                     crop_box_index: int = 0,
                     x: int = 0,
                     y: int = 0,
                     width: int = 0,
                     height: int = 0) -> None:
        assert 0 <= crop_box_index
        assert 0 <= x
        assert 0 <= y
        assert 0 <= width
        assert 0 <= height

        self._crop_number = crop_box_index
        self._x1 = x
        self._y1 = y
        self._width = width
        self._height = height


class HwPreProcInfoBuffer(BufferBase):
    """HwPreProcInfo structure"""
    _pack_ = 4
    _fields_ = [('_img_width', ctypes.c_uint32),
                ('_img_height', ctypes.c_uint32),
                ('_resized_img_width', ctypes.c_uint32),
                ('_resized_img_height', ctypes.c_uint32),
                ('_pad_top', ctypes.c_uint32),
                ('_pad_bottom', ctypes.c_uint32),
                ('_pad_left', ctypes.c_uint32),
                ('_pad_right', ctypes.c_uint32),
                ('_model_input_width', ctypes.c_uint32),
                ('_model_input_height', ctypes.c_uint32),
                ('_crop_area', InfCropBoxBuffer)]

    def _init_buffer(self,
                     img_width: int = 0,
                     img_height: int = 0,
                     resized_img_width: int = 0,
                     resized_img_height: int = 0,
                     pad_top: int = 0,
                     pad_bottom: int = 0,
                     pad_left: int = 0,
                     pad_right: int = 0,
                     model_input_width: int = 0,
                     model_input_height: int = 0,
                     crop_area: InfCropBoxBuffer = InfCropBoxBuffer()) -> None:
        assert 0 <= img_width
        assert 0 <= img_height
        assert 0 <= resized_img_width
        assert 0 <= resized_img_height
        assert 0 <= pad_top
        assert 0 <= pad_bottom
        assert 0 <= pad_left
        assert 0 <= pad_right
        assert 0 <= model_input_width
        assert 0 <= model_input_height

        self._img_width = img_width
        self._img_height = img_height
        self._resized_img_width = resized_img_width
        self._resized_img_height = resized_img_height
        self._pad_top = pad_top
        self._pad_bottom = pad_bottom
        self._pad_left = pad_left
        self._pad_right = pad_right
        self._model_input_width = model_input_width
        self._model_input_height = model_input_height
        self._crop_area = crop_area


class GenericRawResultNDArrayBuffer(BufferBase):
    """GenericRawResultNDArray structure"""
    _pack_ = 4
    _fields_ = [('_LP_raw_out_buffer', ctypes.POINTER(ctypes.c_uint8))]

    def _init_buffer(self, buffer_size: int = 0) -> None:
        self._buffer_size = buffer_size
        self._LP_raw_out_buffer = ctypes.cast((ctypes.c_uint8 * buffer_size)(),
                                              ctypes.POINTER(ctypes.c_uint8))


class GenericInputNodeImageBuffer(BufferBase):
    """GenericInputNodeImage structure"""
    _pack_ = 4
    _fields_ = [('_width', ctypes.c_uint32),
                ('_height', ctypes.c_uint32),
                ('_resize_mode', ctypes.c_uint32),
                ('_padding_mode', ctypes.c_uint32),
                ('_image_format', ctypes.c_uint32),
                ('_normalize_mode', ctypes.c_uint32),
                ('_crop_count', ctypes.c_uint32),
                ('_inf_crop', InfCropBoxBuffer * Const.MAX_CROP_BOX.value),
                ('_image_buffer', ctypes.POINTER(ctypes.c_uint8))]

    def _init_buffer(self,
                     width: int = 0,
                     height: int = 0,
                     resize_mode: ResizeMode = ResizeMode.KP_RESIZE_ENABLE,
                     padding_mode: PaddingMode = PaddingMode.KP_PADDING_CORNER,
                     image_format: ImageFormat = ImageFormat.KP_IMAGE_FORMAT_RGB565,
                     normalize_mode: NormalizeMode = NormalizeMode.KP_NORMALIZE_KNERON,
                     inference_crop_box_buffer_list: List[InfCropBoxBuffer] = [],
                     image_buffer: bytes = bytes()) -> None:
        assert 0 <= width
        assert 0 <= height

        self._width = width
        self._height = height
        self._resize_mode = resize_mode.value
        self._padding_mode = padding_mode.value
        self._image_format = image_format.value
        self._normalize_mode = normalize_mode.value
        self._crop_count = len(inference_crop_box_buffer_list)
        self._inf_crop = (InfCropBoxBuffer * Const.MAX_CROP_BOX.value)(*inference_crop_box_buffer_list)
        self._image_buffer = (ctypes.c_ubyte * len(image_buffer)).from_buffer(bytearray(image_buffer))


class GenericImageInferenceDescriptorBuffer(BufferBase):
    """GenericImageInferenceDescriptor structure"""
    _pack_ = 4
    _fields_ = [('_inference_number', ctypes.c_uint32),
                ('_model_id', ctypes.c_uint32),
                ('_num_input_node_image', ctypes.c_uint32),
                ('_input_node_image_list', GenericInputNodeImageBuffer * Const.MAX_INPUT_NODE_COUNT.value)]

    def _init_buffer(self,
                     inference_number: int = 0,
                     model_id: int = 0,
                     generic_input_node_image_buffer_list: List[GenericInputNodeImageBuffer] = []) -> None:
        assert 0 <= inference_number
        assert 0 <= model_id
        assert Const.MAX_INPUT_NODE_COUNT.value >= len(generic_input_node_image_buffer_list)

        self._inference_number = inference_number
        self._model_id = model_id
        self._num_input_node_image = len(generic_input_node_image_buffer_list)
        self._input_node_image_list = (GenericInputNodeImageBuffer * Const.MAX_INPUT_NODE_COUNT.value)(*generic_input_node_image_buffer_list)


class GenericImageInferenceResultHeaderBuffer(BufferBase):
    """GenericImageInferenceResultHeader structure"""
    _pack_ = 4
    _fields_ = [('_inference_number', ctypes.c_uint32),
                ('_crop_number', ctypes.c_uint32),
                ('_num_output_node', ctypes.c_uint32),
                ('_product_id', ctypes.c_uint32),
                ('_num_pre_proc_info', ctypes.c_uint32),
                ('_hw_pre_proc_info_list', HwPreProcInfoBuffer * Const.MAX_INPUT_NODE_COUNT.value)]

    def _init_buffer(self,
                     inference_number: int = 0,
                     crop_number: int = 0,
                     num_output_node: int = 0,
                     product_id: int = 0,
                     hw_pre_proc_info_list: List[HwPreProcInfoBuffer] = []) -> None:
        assert 0 <= inference_number
        assert 0 <= crop_number
        assert 0 <= num_output_node
        assert 0 <= product_id

        self._inference_number = inference_number
        self._crop_number = crop_number
        self._num_output_node = num_output_node
        self._product_id = product_id
        self._num_pre_proc_info = len(hw_pre_proc_info_list)
        self._hw_pre_proc_info_list = (HwPreProcInfoBuffer * Const.MAX_INPUT_NODE_COUNT.value)(*hw_pre_proc_info_list)


class GenericInputNodeDataBuffer(BufferBase):
    """GenericInputNodeData structure"""
    _pack_ = 4
    _fields_ = [('_buffer_size', ctypes.c_uint32),
                ('_buffer', ctypes.POINTER(ctypes.c_uint8))]

    def _init_buffer(self,
                     buffer: bytes = bytes()) -> None:

        self._buffer_size = len(buffer)
        self._buffer = (ctypes.c_ubyte * self._buffer_size).from_buffer(bytearray(buffer))


class GenericDataInferenceImageHeaderBuffer(BufferBase):
    """GenericDataInferenceImageHeader structure"""
    _pack_ = 4
    _fields_ = [('_inference_number', ctypes.c_uint32),
                ('_model_id', ctypes.c_uint32),
                ('_num_input_node_data', ctypes.c_uint32),
                ('_input_node_data_list', GenericInputNodeDataBuffer * Const.MAX_INPUT_NODE_COUNT.value)]

    def _init_buffer(self,
                     inference_number: int = 0,
                     model_id: int = 0,
                     generic_input_node_data_buffer_list: List[GenericInputNodeDataBuffer] = []) -> None:
        assert 0 <= inference_number
        assert 0 <= model_id
        assert Const.MAX_INPUT_NODE_COUNT.value >= len(generic_input_node_data_buffer_list)

        self._inference_number = inference_number
        self._model_id = model_id
        self._num_input_node_data = len(generic_input_node_data_buffer_list)
        self._input_node_data_list = (GenericInputNodeDataBuffer * Const.MAX_INPUT_NODE_COUNT.value)(*generic_input_node_data_buffer_list)


class GenericDataInferenceResultHeaderBuffer(BufferBase):
    """GenericDataInferenceResultHeader structure"""
    _pack_ = 4
    _fields_ = [('_inference_number', ctypes.c_uint32),
                ('_crop_number', ctypes.c_uint32),
                ('_num_output_node', ctypes.c_uint32),
                ('_product_id', ctypes.c_uint32)]

    def _init_buffer(self,
                     inference_number: int = 0,
                     crop_number: int = 0,
                     num_output_node: int = 0,
                     product_id: int = 0) -> None:
        assert 0 <= inference_number
        assert 0 <= crop_number
        assert 0 <= num_output_node
        assert 0 <= product_id

        self._inference_number = inference_number
        self._crop_number = crop_number
        self._num_output_node = num_output_node
        self._product_id = product_id


class InfFixedNodeOutputBuffer(BufferBase):
    """InfFixedNodeOutputBuffer structure"""
    _pack_ = 4
    _fields_ = [('_width', ctypes.c_uint32),
                ('_height', ctypes.c_uint32),
                ('_channel', ctypes.c_uint32),
                ('_radix', ctypes.c_int32),
                ('_scale', ctypes.c_float),
                ('_factor', ctypes.c_float),
                ('_fixed_point_dtype', ctypes.c_uint32),
                ('_num_data', ctypes.c_uint32),
                ('_data', ctypes.POINTER(ctypes.c_void_p))]

    def _init_buffer(self,
                     width: int = 0,
                     height: int = 0,
                     channel: int = 0,
                     radix: int = 0,
                     scale: float = 0,
                     factor: float = 0,
                     dtype: int = 0,
                     num_data: int = 0,
                     data: np.ndarray = np.array([])) -> None:
        assert 0 <= width
        assert 0 <= height
        assert 0 <= channel
        assert 0 <= num_data

        self._width = width
        self._height = height
        self._channel = channel
        self._radix = radix
        self._scale = scale
        self._factor = factor
        self._fixed_point_dtype = dtype
        self._num_data = num_data
        self._data = data.ctypes.data_as(ctypes.POINTER(ctypes.c_void_p))


class InfFloatNodeOutputBuffer(BufferBase):
    """InfFloatNodeOutputBuffer structure"""
    _pack_ = 4
    _fields_ = [('_width', ctypes.c_uint32),
                ('_height', ctypes.c_uint32),
                ('_channel', ctypes.c_uint32),
                ('_num_data', ctypes.c_uint32),
                ('_data', ctypes.POINTER(ctypes.c_float))]

    def _init_buffer(self,
                     width: int = 0,
                     height: int = 0,
                     channel: int = 0,
                     num_data: int = 0,
                     data: np.ndarray = np.array([])) -> None:
        assert 0 <= width
        assert 0 <= height
        assert 0 <= channel
        assert 0 <= num_data

        self._width = width
        self._height = height
        self._channel = channel
        self._num_data = num_data
        self._data = data.ctypes.data_as(ctypes.POINTER(ctypes.c_float))


class ProfileModelStatisticsBuffer(BufferBase):
    """ProfileModelStatistics structure"""
    _pack_ = 4
    _fields_ = [('_model_id', ctypes.c_uint32),
                ('_inf_count', ctypes.c_uint32),
                ('_cpu_op_count', ctypes.c_uint32),
                ('_avg_pre_process_ms', ctypes.c_float),
                ('_avg_inference_ms', ctypes.c_float),
                ('_avg_cpu_op_ms', ctypes.c_float),
                ('_avg_cpu_op_per_cpu_node_ms', ctypes.c_float),
                ('_avg_post_process_ms', ctypes.c_float)]

    def _init_buffer(self,
                     model_id: int = 0,
                     inference_count: int = 0,
                     cpu_op_count: int = 0,
                     avg_pre_process_ms: float = 0,
                     avg_inference_ms: float = 0,
                     avg_cpu_op_ms: float = 0,
                     avg_cpu_op_per_cpu_node_ms: float = 0,
                     avg_post_process_ms: float = 0) -> None:

        assert 0 <= model_id
        assert 0 <= inference_count
        assert 0 <= cpu_op_count
        assert 0 <= avg_pre_process_ms
        assert 0 <= avg_inference_ms
        assert 0 <= avg_cpu_op_ms
        assert 0 <= avg_cpu_op_per_cpu_node_ms
        assert 0 <= avg_post_process_ms

        self._model_id = model_id
        self._inf_count = inference_count
        self._cpu_op_count = cpu_op_count
        self._avg_pre_process_ms = avg_pre_process_ms
        self._avg_inference_ms = avg_inference_ms
        self._avg_cpu_op_ms = avg_cpu_op_ms
        self._avg_cpu_op_per_cpu_node_ms = avg_cpu_op_per_cpu_node_ms
        self._avg_post_process_ms = avg_post_process_ms


class ProfileDataBuffer(BufferBase):
    """ProfileData structure"""
    _pack_ = 4
    _fields_ = [('_num_model_profiled', ctypes.c_int),
                ('_model_st', ProfileModelStatisticsBuffer * Const.MAX_MODEL_NUM_IN_NEF.value)]

    def _init_buffer(self,
                     model_statistics_buffer_list: List[ProfileModelStatisticsBuffer] = []) -> None:
        assert 0 <= len(model_statistics_buffer_list)
        assert Const.MAX_MODEL_NUM_IN_NEF.value >= len(model_statistics_buffer_list)

        self._num_model_profiled = len(model_statistics_buffer_list)
        self._model_st = (ProfileModelStatisticsBuffer * Const.MAX_MODEL_NUM_IN_NEF.value)(*model_statistics_buffer_list)


class NpuPerformanceMonitorStatisticsBuffer(BufferBase):
    """NpuPerformanceMonitorStatistics structure"""
    _pack_ = 4
    _fields_ = [('_model_id', ctypes.c_uint32),
                ('_npu_clock_rate', ctypes.c_uint32),
                ('_f0', ctypes.c_uint32),
                ('_f1', ctypes.c_uint32),
                ('_f2', ctypes.c_uint32),
                ('_f3', ctypes.c_uint32),
                ('_f4', ctypes.c_uint32),
                ('_f5', ctypes.c_uint32),
                ('_f6', ctypes.c_uint32),
                ('_f7', ctypes.c_uint32)]

    def _init_buffer(self,
                     model_id: int = 0,
                     npu_clock_rate: int = 0,
                     f0: int = 0,
                     f1: int = 0,
                     f2: int = 0,
                     f3: int = 0,
                     f4: int = 0,
                     f5: int = 0,
                     f6: int = 0,
                     f7: int = 0,) -> None:

        assert 0 <= model_id
        assert 0 <= npu_clock_rate
        assert 0 <= f0
        assert 0 <= f1
        assert 0 <= f2
        assert 0 <= f3
        assert 0 <= f4
        assert 0 <= f5
        assert 0 <= f6
        assert 0 <= f7

        self._model_id = model_id
        self._npu_clock_rate = npu_clock_rate
        self._f0 = f0
        self._f1 = f1
        self._f2 = f2
        self._f3 = f3
        self._f4 = f4
        self._f5 = f5
        self._f6 = f6
        self._f7 = f7


class PerformanceMonitorDataBuffer(BufferBase):
    """PerformanceMonitorData structure"""
    _pack_ = 4
    _fields_ = [('_num_model_profiled', ctypes.c_int),
                ('_model_st', NpuPerformanceMonitorStatisticsBuffer * Const.MAX_MODEL_NUM_IN_NEF.value)]

    def _init_buffer(self,
                     model_statistics_buffer_list: List[NpuPerformanceMonitorStatisticsBuffer] = []) -> None:
        assert 0 <= len(model_statistics_buffer_list)
        assert Const.MAX_MODEL_NUM_IN_NEF.value >= len(model_statistics_buffer_list)

        self._num_model_profiled = len(model_statistics_buffer_list)
        self._model_st = (NpuPerformanceMonitorStatisticsBuffer * Const.MAX_MODEL_NUM_IN_NEF.value)(*model_statistics_buffer_list)


class BoundingBoxBuffer(BufferBase):
    """BoundingBoxBuffer structure"""
    _pack_ = 4
    _fields_ = [('_x1', ctypes.c_float),
                ('_y1', ctypes.c_float),
                ('_x2', ctypes.c_float),
                ('_y2', ctypes.c_float),
                ('_score', ctypes.c_float),
                ('_class_num', ctypes.c_int32)]

    def _init_buffer(self,
                     x1: float = 0,
                     y1: float = 0,
                     x2: float = 0,
                     y2: float = 0,
                     score: float = 0,
                     class_num: int = 0) -> None:
        assert 0 <= x1
        assert 0 <= y1
        assert 0 <= x2
        assert 0 <= y2

        self._x1 = x1
        self._y1 = y1
        self._x2 = x2
        self._y2 = y2
        self._score = score
        self._class_num = class_num


class PointBuffer(BufferBase):
    """PointBuffer structure"""
    _pack_ = 4
    _fields_ = [('_x', ctypes.c_uint32),
                ('_y', ctypes.c_uint32)]

    def _init_buffer(self,
                     x: float = 0,
                     y: float = 0) -> None:
        assert 0 <= x
        assert 0 <= y

        self._x = x
        self._y = y


class LandMarkBuffer(BufferBase):
    """LandMarkBuffer structure"""
    _pack_ = 4
    _fields_ = [('_marks', PointBuffer * Const.LAND_MARK_POINTS.value),
                ('_score', ctypes.c_float),
                ('_blur', ctypes.c_float),
                ('_class_num', ctypes.c_int32)]

    def _init_buffer(self,
                     point_list: List[PointBuffer] = [],
                     score: float = 0,
                     blur: float = 0,
                     class_num: int = 0) -> None:
        self._marks = (PointBuffer * Const.LAND_MARK_POINTS.value)(*point_list)
        self._score = score
        self._blur = blur
        self._class_num = class_num


class ClassificationBuffer(BufferBase):
    """ClassificationBuffer structure"""
    _pack_ = 4
    _fields_ = [('_class_num', ctypes.c_uint32),
                ('_score', ctypes.c_float)]

    def _init_buffer(self,
                     class_num: int = 0,
                     score: float = 0) -> None:
        self._class_num = class_num
        self._score = score


class FaceRecognizeBuffer(BufferBase):
    """FaceRecognizeBuffer structure"""
    _pack_ = 4
    _fields_ = [('_feature_map', ctypes.c_float * Const.FR_FEAT_LENGTH.value),
                ('_feature_map_fixed', ctypes.c_int8 * Const.FR_FEAT_LENGTH.value)]

    def _init_buffer(self,
                     feature_map: np.ndarray = np.zeros((Const.FR_FEAT_LENGTH.value,), dtype=np.float32),
                     feature_map_fixed: np.ndarray = np.zeros((Const.FR_FEAT_LENGTH.value,), dtype=np.int8)):
        assert feature_map.shape[0] == Const.FR_FEAT_LENGTH.value
        assert feature_map_fixed.shape[0] == Const.FR_FEAT_LENGTH.value

        self._feature_map = np.ctypeslib.as_ctypes(feature_map.reshape(-1))
        self._feature_map_fixed = np.ctypeslib.as_ctypes(feature_map_fixed.reshape(-1))
